#ifndef __wMENU_H__
#define __wMENU_H__

#if !WINDOWS_SYSTEM
	#error only windows system supported
#endif

#ifndef __ETLBASE_H__
	#error etlmenu.h requires etlbase.h to be included first
#endif

namespace ETL_MenuAbout
{
class wMenuPop : public CMenu
{
public:
	chTempString GetMenuString(UINT nIDItem, UINT nFlags)
	{
		chTempString strText;
		int nTextLen = ::GetMenuString(m_hMenu, nIDItem, NULL, 0, nFlags);
		strText.SetSize(nTextLen);
		::GetMenuString(m_hMenu, nIDItem, strText.GetData(), nTextLen, MF_BYPOSITION);
		return strText;
	}
	
public:
	UINT GetMenuItemData(UINT nPos);
	BOOL TrackPopupMenu(UINT nFlags, int x, int y, HWND hWnd, LPCRECT lpRect = NULL);
	BOOL LoadMenu(UINT nIDResource);
	BOOL BindWindow(HWND hWnd);
	BOOL UnBindWindow();
	BOOL RenderMenu(HMENU hMenu);
	
	// Overridables (must override draw and measure for owner-draw menu items)
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
protected:
	UINT GetIndexByData(DWORD dwData);
//	WNDPROC m_oldWndProc;
private:
	static chObjList<wMenuPop*> m_MenuList;
	static chObjMap<HWND, WNDPROC> m_WndProcMap;
	static LRESULT Menu_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
	
public:
	wMenuPop(CMenu* pMenu = NULL)
	{
		m_hPopWnd = NULL;
		if(pMenu != NULL)
			RenderMenu(pMenu->Detach());
	}
	~wMenuPop()
	{
		UnBindWindow();
		while(m_SubPopMenuList.size() > 0)
		{
			delete m_SubPopMenuList.front();
			m_SubPopMenuList.pop_front();
		}
	}
protected:
	struct MENU_ITEM
	{
		DWORD	dwExtraData;	// for child clss use,such as LCBmpMenu
	};
	chObjList<MENU_ITEM> m_ItemDataList;
	chObjList<wMenuPop*> m_SubPopMenuList;
	HWND m_hPopWnd;
};

class wMenuBMP : public wMenuPop  
{
public:
	// Overridables (must override draw and measure for owner-draw menu items)
	BOOL SetItemBmp(UINT nPos, UINT nIDRes);
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);

public:
	wMenuBMP()
	{m_nLeftBarWidth = -1;}
	~wMenuBMP(){}
protected:
	int m_nLeftBarWidth;
};

inline void wMenuPop::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
	chASSERT(lpDIS->CtlType == ODT_MENU);
//	MENU_ITEM& rItem = m_ItemDataList.GetAt((POSITION)lpDIS->itemData);
	
	int nHeight = lpDIS->rcItem.bottom - lpDIS->rcItem.top;
//	if(lpDIS->itemID == 0)// separator
//	{
//		lpDIS->rcItem.top += nHeight / 2 - 1;
//		::DrawEdge(lpDIS->hDC, &lpDIS->rcItem, EDGE_ETCHED, BF_TOP);
//	}
//	else
	{
		RECT rectText = lpDIS->rcItem;
		rectText.left += nHeight;
//		InflateRect(&rectText, -nHeight, 0);
		RECT rectEnti = lpDIS->rcItem;

		UINT nPos = GetIndexByData(lpDIS->itemData);
		int nTextLen = ::GetMenuString(m_hMenu, nPos, NULL, 0, MF_BYPOSITION);
		chString strText;
		::GetMenuString(m_hMenu, nPos, strText.GetBufferSetLength(nTextLen), nTextLen, MF_BYPOSITION);
		
		BOOL bSelected = (lpDIS->itemState & ODS_SELECTED) == ODS_SELECTED;
		BOOL bGray = (lpDIS->itemState & ODS_GRAYED) == ODS_GRAYED;
		int crText = 0;
		int crBk = 0;

		if(bSelected)
		{
			crBk = COLOR_HIGHLIGHT;
			if(bGray)
				crText = COLOR_GRAYTEXT;
			else
				crText = COLOR_HIGHLIGHTTEXT;
		}
		else
		{
			crBk = COLOR_MENU;
			if(bGray)
				crText = COLOR_HIGHLIGHTTEXT;
			else
				crText = COLOR_MENUTEXT;
		}
		int oldMode = ::SetBkMode(lpDIS->hDC, TRANSPARENT);
		::FillRect(lpDIS->hDC, &rectEnti, GetSysColorBrush(crBk));
		COLORREF oldCol = ::SetTextColor(lpDIS->hDC, GetSysColor(crText));
		{
			::DrawText(lpDIS->hDC, strText.GetData(), strText.GetSize(), &rectText, DT_SINGLELINE|DT_VCENTER|DT_EXPANDTABS);
			if(!bSelected && bGray)
			{
				::SetTextColor(lpDIS->hDC, GetSysColor(COLOR_GRAYTEXT));
				OffsetRect(&rectText, -1, -1);
				::DrawText(lpDIS->hDC, strText.GetData(), strText.GetSize(), &rectText, DT_SINGLELINE|DT_VCENTER|DT_EXPANDTABS);
			}
		}
		::SetTextColor(lpDIS->hDC, oldCol);
		::SetBkMode(lpDIS->hDC, oldMode);
	}
}

inline void wMenuPop::MeasureItem(LPMEASUREITEMSTRUCT lpMIS)
{
	SYZ_TRACE(_T("LCMenuItem::MeasureItem"));
//	MENU_ITEM& rItem = m_ItemDataList.GetAt((POSITION)lpMIS->itemData);
//	if(lpMIS->itemID == 0)// separator
//	{
//		lpMIS->itemWidth = 0;
//		lpMIS->itemHeight = ::GetSystemMetrics(SM_CYMENU)>>1;
//	}
//	else
	{
		NONCLIENTMETRICS nm;
		nm.cbSize = sizeof (NONCLIENTMETRICS);
		SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize, &nm, 0);
		CFont fontMenu;
		fontMenu.CreateFontIndirect(&nm.lfMenuFont);

		// Obtain the width of the text:
		HWND hWnd = GetActiveWindow();            // Get main window
		if (hWnd == NULL)
			hWnd = ::GetDesktopWindow();
		LCDCWindowTemp tmpDC(hWnd);
		LCGdiSelect tFontSelect(tmpDC.m_hDC, (HFONT)fontMenu);
			
		//Get pointer to text SK
		UINT nThisPos = GetIndexByData(lpMIS->itemData);
		chTempString strText = GetMenuString(nThisPos, MF_BYPOSITION);
		
		SIZE size = {0, 0};
		::GetTextExtentPoint32(tmpDC.m_hDC, strText.GetData(), strText.GetSize(), &size); //SK should also work on 95

		// Set width and height:
		lpMIS->itemHeight = 20;//GetSystemMetrics(SM_CYMENU) + 1;
		lpMIS->itemWidth = size.cx + lpMIS->itemHeight + lpMIS->itemHeight;
	}
}

inline BOOL wMenuPop::RenderMenu(HMENU hMenu)
{
	Attach(hMenu);
	UINT nItemCount = GetMenuItemCount();
	
	UINT i = 0;
	for(i = 0; i < nItemCount; i++)
	{
		UINT itemID = GetMenuItemID(i);
		MENU_ITEM tItem = {itemID};
		m_ItemDataList.push_back(tItem);

		UINT nFlag = GetMenuState(i, MF_BYPOSITION);
		if(nFlag & MF_POPUP)
		{
			HMENU hSubMenu = ::GetSubMenu(hMenu, i);

			wMenuPop* pSubMenu = new wMenuPop; // new is can't work correctly here ? think it yourself
//			wMenuPop* pSubMenu = (wMenuPop*)GetRuntimeClass()->CreateObject();
			m_SubPopMenuList.push_back(pSubMenu);
			pSubMenu->RenderMenu(hSubMenu);

			ModifyMenu(i, MF_OWNERDRAW|MF_BYPOSITION|nFlag, (UINT)hSubMenu, (LPCTSTR)pos);
		}
		else 
			if((nFlag & MF_SEPARATOR) != MF_SEPARATOR)
		{
			ModifyMenu(i, MF_OWNERDRAW|MF_BYPOSITION|nFlag, itemID, (LPCTSTR)pos);
		}
	}
	return TRUE;
}

inline UINT wMenuPop::GetIndexByData(DWORD dwData)
{
	UINT nIxdex = 0;
	for(POSITION pos = m_ItemDataList.GetHeadPosition(); pos != NULL; m_ItemDataList.GetNext(pos))
	{
		if((DWORD)pos == dwData)
			break;
		nIxdex ++;
	}
	
	return pos == NULL ? -1 : nIxdex;
}

inline BOOL wMenuPop::BindWindow(HWND hWnd)
{
	ASSERT(IsWindow(hWnd));
	if(!IsWindow(hWnd))
		return FALSE;
	if(m_hPopWnd != NULL)
		UnBindWindow();

	m_hPopWnd = hWnd;
	
	wMenuPop* pSameWndMenu = NULL;
	for(POSITION pos = m_MenuList.GetHeadPosition(); pos != NULL; m_MenuList.GetNext(pos))
	{
		wMenuPop* pMenu = m_MenuList.GetAt(pos);
		if(pMenu->m_hPopWnd == m_hPopWnd)
			pSameWndMenu = pMenu;
	}
	
	if(pSameWndMenu != NULL)
	{
		NULL;// nothing to do
	}
	else
	{
		ASSERT(GetWindowLong(hWnd, GWL_WNDPROC) != (LONG)Menu_WindowProc);
		{
			WNDPROC oldProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)Menu_WindowProc);
			m_WndProcMap.SetAt(m_hPopWnd, oldProc);
		}
	}
	m_MenuList.AddHead(this);
	return TRUE;
}

inline BOOL wMenuPop::UnBindWindow()
{
	if(!IsWindow(m_hPopWnd))
		return FALSE;
	
	POSITION pos = m_MenuList.Find(this);
	m_MenuList.RemoveAt(pos);
	
	wMenuPop* pSameWndMenu = NULL;
	for(pos = m_MenuList.GetHeadPosition(); pos != NULL; m_MenuList.GetNext(pos))
	{
		wMenuPop* pMenu = m_MenuList.GetAt(pos);
		if(pMenu->m_hPopWnd == m_hPopWnd)
			pSameWndMenu = pMenu;
	}
	
	if(pSameWndMenu != NULL)
	{
		NULL;// nothing to do
	}
	else
	{
		WNDPROC oldProc = NULL;
		if(m_WndProcMap.Lookup(m_hPopWnd, oldProc))
		{
			SetWindowLong(m_hPopWnd, GWL_WNDPROC, (LONG)oldProc);
			m_WndProcMap.RemoveKey(m_hPopWnd);
		}
		ASSERT(oldProc != NULL);
	}
	m_hPopWnd = NULL;
	return TRUE;
}

inline CMenu* GetMenuFromItemID(CMenu* pMenu, UINT nItemID)
{
	UINT nItems = pMenu->GetMenuItemCount();
	for (int iItem = 0; iItem < (int)nItems; iItem++)
	{
		CMenu* pPopup = pMenu->GetSubMenu(iItem);
		if (pPopup != NULL)
		{
			if((UINT)pPopup->GetSafeHmenu() == nItemID)
				return pMenu;
			// recurse to child popup
			pPopup = GetMenuFromItemID(pPopup, nItemID);
			// check popups on this popup
			if (pPopup != NULL)
				return pPopup;
		}
		else if (pMenu->GetMenuItemID(iItem) == nItemID)
		{
			// it is a normal item inside our popup
			pMenu = CMenu::FromHandlePermanent(pMenu->m_hMenu);
			return pMenu;
		}
	}
	// not found
	return NULL;
}

inline LRESULT wMenuPop::Menu_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	WNDPROC oldProc = NULL;
	m_WndProcMap.Lookup(hWnd, oldProc);
	ASSERT(oldProc != NULL);
	
	switch(uMsg) 
	{
	case WM_MEASUREITEM:
		{
			LPMEASUREITEMSTRUCT lpMIS = (LPMEASUREITEMSTRUCT)lParam;
			if (lpMIS->CtlType == ODT_MENU)
			{
				ASSERT(lpMIS->CtlID == 0);
				for(POSITION pos = m_MenuList.GetHeadPosition(); pos != NULL; m_MenuList.GetNext(pos))
				{
					wMenuPop* pMenu = m_MenuList.GetAt(pos);
					if(pMenu->m_hPopWnd == hWnd)
					{
						CMenu* pMenuOK = GetMenuFromItemID(pMenu, lpMIS->itemID);
						if(pMenuOK != NULL)
						{
							pMenuOK->MeasureItem(lpMIS);
							return TRUE;
						}
					}
				}
			}
		}
		break;
	}
	return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}

inline BOOL wMenuPop::LoadMenu(UINT nIDResource)
{
	return RenderMenu(::LoadMenu(AfxFindResourceHandle(
		MAKEINTRESOURCE(nIDResource), RT_MENU), MAKEINTRESOURCE(nIDResource)));
}

inline BOOL wMenuPop::TrackPopupMenu(UINT nFlags, int x, int y, CWnd *pWnd, LPCRECT lpRect)
{
	BOOL bIsPopMenu = IsKindOf(RUNTIME_CLASS(wMenuPop));
	if(bIsPopMenu)
		BindWindow(pWnd->GetSafeHwnd());
	BOOL re = CMenu::TrackPopupMenu(nFlags, x, y, pWnd, lpRect);
	if(bIsPopMenu)
		UnBindWindow();
	return re;
}

inline UINT wMenuPop::GetMenuItemData(UINT nPos)
{
	MENUITEMINFO ItemInfo = {sizeof(MENUITEMINFO)};
	ItemInfo.fMask = MIIM_DATA;
	if(!GetMenuItemInfo(nPos, &ItemInfo, TRUE))
		return 0;
	return ItemInfo.dwItemData;
}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define OEMRESOURCE
inline void wMenuBMP::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
	UINT iThisPos = GetIndexByData(lpDIS->itemData);
	MENU_ITEM& rITem = m_ItemDataList.GetAt((POSITION)lpDIS->itemData);

	if(iThisPos == 0 && m_nLeftBarWidth > 0)
	{
		CDC* pDC = CDC::FromHandle(lpDIS->hDC);
		CRect rectBmp = lpDIS->rcItem;
		pDC->GetClipBox(rectBmp);
		rectBmp.right = lpDIS->rcItem.right;// + 4;
		LCBitmap LeftBmp;
		if(LeftBmp.CreateGradientBMP(rectBmp.Width(), rectBmp.Height(), FALSE, RGB(255,255,255), RGB(0,0,0)))
		{
			CString strText;
			GetMenuString(0, strText, MF_BYPOSITION);
			LeftBmp.Draw(pDC, rectBmp.left, rectBmp.top);
			CFont	vertFont;
			vertFont.CreateFont(rectBmp.Width(), 0, 900, 000, FW_BOLD,
				0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
				CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
				DEFAULT_PITCH, _T("Arial"));
			CFont *pOldFont = pDC->SelectObject(&vertFont);
			COLORREF oldColor = pDC->SetTextColor(RGB(255, 255, 255));
			int nOldMode = pDC->SetBkMode(TRANSPARENT);
			pDC->TextOut(rectBmp.left, rectBmp.bottom - 4, strText);
			pDC->SetTextColor(oldColor);	
			pDC->SelectObject(pOldFont);
			pDC->SetBkMode(nOldMode);
		}
// delete for use text left
//		if(LeftBmp.LoadBitmap(rITem.dwExtraData))
//		{
//			for(int nTop = 0; nTop < Rect.bottom; nTop += LeftBmp.GetPixelSize().cy)
//				LeftBmp.Draw(pDC, 0, nTop);
//		}
	}
	else
	{
		wMenuPop::DrawItem(lpDIS);
		CRect rectBmp(&lpDIS->rcItem);
		rectBmp.right = rectBmp.left + rectBmp.Height();
		LCBitmap ItemBmp;
		if(ItemBmp.LoadBitmap(rITem.dwExtraData))
		{
			CDC* pDC = CDC::FromHandle(lpDIS->hDC);
// delete for no need show trans Item bimap
			ItemBmp.DrawTrans(pDC, rectBmp.left + 2, rectBmp.top + 2, RGB(192, 192, 192));
//			ItemBmp.Draw(pDC, rectBmp.left + 2, rectBmp.top + 2);
		}
	}
}

inline void wMenuBMP::MeasureItem(LPMEASUREITEMSTRUCT lpMIS)
{
	if(m_nLeftBarWidth == -1)
	{
		if(GetMenuItemCount() > 1 && GetMenuState(1, MF_BYPOSITION) & MF_MENUBREAK)
		{
// delete for use text left
//			MENU_ITEM& rItem = m_ItemDataList.GetHead();
//			LCBitmap Bmp;
//			if(!Bmp.LoadBitmap(rItem.dwExtraData))
//				m_nLeftBarWidth = 0;
//			m_nLeftBarWidth = Bmp.GetPixelSize().cx;
			m_nLeftBarWidth = 20;
		}
		else
			m_nLeftBarWidth = 0;
	}
	
	UINT iThisPos = GetIndexByData(lpMIS->itemData);
	
	if(m_nLeftBarWidth > 0 && iThisPos == 0 )
	{
		lpMIS->itemWidth = m_nLeftBarWidth - 14;
	}
	else
	{
		wMenuPop::MeasureItem(lpMIS);
	}
}

inline BOOL wMenuBMP::SetItemBmp(UINT nPos, UINT nIDRes)
{
	MENUITEMINFO ItemInfo = {sizeof(MENUITEMINFO)};
	ItemInfo.fMask = MIIM_DATA;
	if(!GetMenuItemInfo(nPos, &ItemInfo, TRUE))
		return FALSE;
	
	MENU_ITEM& rItem = m_ItemDataList.GetAt((POSITION)ItemInfo.dwItemData);
	rItem.dwExtraData = nIDRes;
	return TRUE;
}
};
using namespace ETL_MenuAbout;

#endif //__wMENU_H__